![]() |
![]() |
|
Eine Auswahlabfrage basiert auf dem SELECT-Statement und liefert immer ein Ergebnis zurück. Dazu gehören auch die Abfragen, die eine Aggregatfunktion wie SUM oder COUNT aufrufen, die nur einen Ergebniswert liefern. Eine typische Auswahlabfrage wäre zum Beispiel:
Das Resultat dieser Abfrage bilden alle Datensätze der Tabelle authors, welche die Autoren beschreiben, die im US-Bundesstaat California leben. Eine Aktionsabfrage manipuliert die Datenbank. Bei der Manipulation kann es sich
handeln. Mit
wird beispielsweise eine DML-Abfrage abgesetzt, die zwar einige Datensätze in authors ändert, selbst aber keine Ergebnismenge liefert. Wie Sie sehen, führt das Absetzen eines Befehls zu ganz unterschiedlichen Reaktionen des Datenbankservers. Ein Command-Objekt trägt dem Rechnung und stellt mit
drei Methoden zur Verfügung, die speziell auf die einzelnen Abfragen abgestimmt sind. 26.3.3 Aktionsabfragen mit »ExecuteNonQuery« absetzen
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Public Function ExecuteNonQuery() As Integer |
Handelt es sich bei dem Befehl um ein UPDATE-, INSERT- oder DELETE-Kommando, können Sie über den Rückgabewert feststellen, wie viele Datenzeilen verändert worden sind.
Im folgenden Beispielprogramm wird der Tabelle authors ein Datensatz hinzugefügt. Dabei wird der Konstruktor der Klasse SqlCommand verwendet, der sowohl den SQL-Befehl als auch die Referenz auf das Connection-Objekt entgegennehmen kann. Vor dem Absetzen des SQL-Kommandos muss die Verbindung zu der betreffenden Datenbank geöffnet werden, nach der Ausführung wird sie wieder geschlossen.
| ' ---------------------------------------------------------- |
| ' Beispiel: ...\Kapitel 26\ExecuteNonQueryDemo |
| ' ---------------------------------------------------------- |
| Imports System.Data.SqlClient |
| Module Module1 |
| Sub Main() |
| Dim con As SqlConnection = _ |
| New SqlConnection("Data Source=(local);" & _ |
| "Initial Catalog=pubs;Trusted_Connection=Yes") |
| ' SQL-Befehl |
| Dim strSQL As String = _ |
| "INSERT INTO authors(au_id,au_lname, au_fname,contract) " & _ |
| "VALUES('111–11–1111','Mueller','Peter',0)" |
| Try |
| con.Open() |
| Dim cmd As SqlCommand = New SqlCommand(strSQL, con) |
| ' Kommando absetzen |
| cmd.ExecuteNonQuery() |
| Catch e As Exception |
| Console.WriteLine("Fehlermeldung: {0}", e.Message) |
| End Try |
| con.Close() |
| End Sub |
| End Module |
Dass alle fehlerträchtigen Anweisungen in einem Try-Block implementiert sind, ist hier besonders wichtig. Führen Sie nämlich das Programm zweimal hintereinander aus, wird ein Laufzeitfehler ausgelöst. Der Grund ist sehr einfach: Bei dem ersten Versuch wird der Datensatz erfolgreich hinzugefügt, beim zweiten Versuch wird gegen die Primärschlüsseleinschränkung verstoßen, da keine zwei Zeilen in der Tabelle im Primärschlüsselfeld den gleichen Wert haben dürfen (in authors ist au_id das Primärschlüsselfeld).
Mit der Methode ExecuteNonQuery des Command-Objekts können Sie Datensätze in der Originaldatenbank manipulieren. Manchmal möchte man jedoch auch die Datensätze einer Tabelle in einer Clientanwendung anzeigen lassen. Jetzt betritt eine andere Methode die Bühne im Codeeditor: ExecuteReader.
| Public Function ExecuteReader() As SqlDataReader |
Das Ergebnis des Methodenaufrufs ist ein Objekt vom Typ SqlDataReader. Dieses ähnelt den anderen Reader-Objekten des .NET Frameworks (TextReader, StreamReader usw.). Ein DataReader-Objekt enthält eine Datensatzgruppe, die allerdings schreibgeschützt ist. Beabsichtigen Sie, an den Datensätzen Änderungen vorzunehmen, ist der DataReader völlig ungeeignet. Vielmehr liegt die Stärke in seiner Ausführungsgeschwindigkeit. Damit ist auch bereits das Haupteinsatzgebiet abgedeckt: Ein DataReader bietet sich hauptsächlich an, wenn Komponenten wie List- oder Comboboxen gefüllt werden sollen.
Das Erzeugen eines DataReader-Objekts funktioniert nur über den Aufruf der Methode ExecuteReader, denn die Klasse SqlDataReader weist keinen öffentlichen Konstruktor auf.
| Dim dr As SqlDataReader = cmd.ExecuteReader() |
Im folgenden Beispielprogramm wird ein SqlDataReader dazu benutzt, die Vor- und Zunamen sowie den Wohnort aller Autoren der Tabelle authors nach den Zunamen sortiert auszugeben.
| ' ---------------------------------------------------------- |
| ' Beispiel: ...\Kapitel 26\ExecuteReaderDemo |
| ' ---------------------------------------------------------- |
| Imports System.Data.SqlClient |
| Module Module1 |
| Sub Main() |
| Dim con As SqlConnection = New SqlConnection( _ |
| "Data Source=.;Initial Catalog=pubs;Trusted_Connection=yes") |
| con.Open() |
| Dim strSQL As String = _ |
| "SELECT au_lname, au_fname, city " & _ |
| "FROM authors " & _ |
| "ORDER BY[au_lname]" |
| Dim cmd As SqlCommand = New SqlCommand(strSQL, con) |
| Dim dr As SqlDataReader = cmd.ExecuteReader() |
| Do While (dr.Read()) |
| Console.WriteLine("{0,-20}{1,-20}{2,-20}", _ |
| dr("au_lname"), dr("au_fname"), dr("city")) |
| Loop |
| dr.Close() |
| con.Close() |
| Console.ReadLine() |
| End Sub |
| End Module |
Nach dem Öffnen der Verbindung zur Datenquelle wird zuerst die Zeichenfolge des SELECT-Statements definiert, die im nächsten Schritt zusammen mit der Referenz auf das SqlConnection-Objekt dazu dient, ein Command-Objekt zu erzeugen. Auf das Command-Objekt wird dann die Methode ExecuteReader ausgeführt und der Rückgabewert in der Referenz dr vom Typ SqlDataReader gespeichert.
Im SqlDataReader sind alle Datensätze enthalten, die nun der Reihe nach durchlaufen werden. Um die einzelnen Datensätze abzurufen, gibt es die Methode Read des SqlDataReaders. Die Methode setzt den SqlDataReader beim ersten Aufruf auf die erste Datenzeile der Ergebnismenge, bei jedem Aufruf auf die darauf folgende. Der Rückgabewert ist True, wenn noch weitere Datenzeilen abgerufen werden können. Ist der Rückgabewert False, ist kein Datensatz mehr verfügbar. Damit eignet sich Read, um durch die Datensatzliste in einer Do-While-Schleife vom ersten bis zum letzten Datensatz zu laufen.
Mit jedem Aufruf von Read wird der DataReader um eine Datenzeile verschoben. Jede Datenzeile in unserem Beispiel hat drei Feldinformationen, nämlich die der Spalten au_lname, au_fname und city. Die einzelnen Spalten einer Abfrage werden in einer Auflistung geführt, auf die über den Indexer des DataReader-Objekts zugegriffen werden kann. Da der Indexer überladen ist, kann man entweder den Spaltenbezeichner als Zeichenfolge angeben oder die Spaltenordnungszahl als Integer. Damit sind, bezogen auf unser Beispiel, die beiden Anweisungen
| dr("au_lname") |
| dr(0) |
gleichwertig, denn die Spalte au_lname steht in der Abfragezeichenfolge an erster Stelle.
Ein DataReader hat eine typspezifische Eigenschaft, die unbedingt beachtet werden muss: Er blockiert das Verbindungsobjekt. Solange der SqlDataReader geöffnet ist, können keinen anderen Aktionen auf Basis der Verbindung durchgeführt werden, noch nicht einmal das Öffnen eines zweiten SqlDataReaders. Daher sollte die Sperre so schnell wie möglich mit
| dr.Close() |
Mit der SELECT-Anweisung können Sie eine Datensatzliste nach bestimmten Auswahlkriterien aus einer Datenbank abrufen. SELECT wird zusätzlich für Aggregatfunktionen benutzt, die ein Ergebnis zurückliefern. Beispielsweise können Sie mit
| SELECT COUNT(*) FROM authors |
die Anzahl der Autoren in der Tabelle authors ermitteln. Mit
| SELECT COUNT(*) FROM authors WHERE city='Oakland' |
stellen Sie fest, wie viele Autoren in Oakland wohnen. Neben COUNT stehen noch weitere Aggregatfunktionen zur Verfügung: SUM, um die Summe eines numerischen Ausdrucks zu ermitteln, AVG, um einen Durchschnittswert zu bilden, sowie MIN und MAX, um aus einem gegebenen Ausdruck den Maximal- bzw. Minimalwert zu erhalten.
Um den Rückgabewert einer Aggregatfunktion entgegenzunehmen, muss die Methode ExecuteScalar auf das Command-Objekt aufgerufen werden. Der Rückgabetyp ist Object, daher muss man das Ergebnis noch passend konvertieren, z. B.:
| Dim strSQL As String = "SELECT COUNT(*) FROM authors " & _ |
| "WHERE city='Oakland'" |
| Dim cmd As SqlCommand = New SqlCommand(strSQL, con) |
| Dim anzahlDS As Integer= cmd.ExecuteScalar() |
Dieses Kommando liefert als Ergebnis in der Variablen anzahlDS die Anzahl der Autoren, die in Oakland wohnen.
Die konkrete Suche nach einem bestimmten Datensatz in einer Tabelle wird durch die WHERE-Klausel einer SELECT-Abfrage bestimmt:
| SELECT * FROM authors WHERE au_lname = 'Dull' |
Unstrittig ist, dass die Hartcodierung dieser Abfrage nicht anwendergerecht ist. Was ist, wenn der Benutzer nicht nach dem Autor »Dull« suchen möchte, sondern je nach Ausgangssituation die Informationen über einen anderen Autor benötigt? Die Abfrage muss in diesem Fall allgemeiner formuliert werden, und zwar so, dass der Anwender zur Laufzeit des Programms selbst den Autor bestimmen kann.
Die Lösung lautet: parametrisierte Abfrage. Wir müssen bei den folgenden Ausführungen jedoch berücksichtigen, dass die Wahl des .NET-Datenproviders maßgeblich die Syntax des SELECT-Statements und des Programmcodes einer parametrisierten Abfrage beeinflusst.
Eine typische parametrisierte Abfrage ist die folgende:
| SELECT * FROM authors WHERE au_lname = @Zuname |
Platzhalter des SqlClient-Datenproviders setzen das Präfix »@« voraus. @Zuname ist hier der Parameter in der Abfrage, der zur Laufzeit mit einem Wert gefüllt werden muss.
| Hinweis |
|
Die Odbc- und OleDb-Datenprovider benutzen das Fragezeichen als Platzhalter. |
Es ist auch kein Problem, mehrere Parameter festzulegen. Wollen Sie sich beispielsweise alle Autoren ausgeben lassen, die einen bestimmten Zunamen haben oder in einer bestimmten Stadt wohnen, lautet das SQL-Statement:
| SELECT au_lname, city |
| FROM authors |
| WHERE au_lname = @Zuname OR city = @City |
Zuerst wollen wir uns ansehen, wie diese Abfrage in einem Programm abgesetzt wird.
| ' ------------------------------------------------------ |
| ' Beispiel: ...\Kapitel 26\ParametrisierteAbfrage |
| ' ------------------------------------------------------ |
| Imports System.Data.SqlClient |
| Module Module1 |
| Sub Main() |
| Dim con As SqlConnection = _ |
| New SqlConnection("Data Source=(local);" & _ |
| "Initial Catalog=pubs;Trusted_Connection=Yes") |
| con.Open() |
| Dim strSQL As String = _ |
| "SELECT au_lname, city FROM authors " & _ |
| "WHERE au_lname = @Zuname OR city = @City" |
| Dim cmd As SqlCommand = New SqlCommand(strSQL, con) |
| ' Parameter definieren |
| cmd.Parameters.Add("@Zuname", SqlDbType.VarChar, 40) |
| cmd.Parameters.Add("@City", SqlDbType.VarChar, 20) |
| ' Parameter füllen |
| cmd.Parameters("@Zuname").Value = "Dull" |
| cmd.Parameters("@City").Value = "Oakland" |
| ' Kommando ausführen |
| Dim dr As SqlDataReader = cmd.ExecuteReader() |
| ' Datensätze anzeigen |
| Do While (dr.Read()) |
| Console.WriteLine("Zuname: {0,-20} Stadt: {1,-20}", _ |
| dr(0), dr(1)) |
| Loop |
| dr.Close() |
| con.Close() |
| Console.ReadLine() |
| End Sub |
| End Module |
Jedes SqlCommand-Objekt verfügt über eine Auflistung, in der alle Parameter einer Abfrage enthalten sind. Die Eigenschaft Parameters des Command-Objekts liefert die Referenz auf eine Auflistung vom Typ SqlParameterCollection.
Solange nicht ausdrücklich Parameter hinzugefügt werden, ist die Auflistung leer. Ein Parameter wird unter Aufruf der Methode Add hinzugefügt. Add ist vielfach überladen. Im Beispielprogramm wurde die folgende Überladung eingesetzt:
| Public Function Add(String, SqlDbType, Integer) As SqlParameter |
Zurückgegeben wird die Referenz auf ein SqlParameter-Objekt, das dann interessant ist, wenn man die Eigenschaften des Parameters auswerten oder vor dem Absetzen des SQL-Kommandos manipulieren möchte. Am Ende dieses Kapitels werden wir bei der Betrachtung der Aktualisierung darauf noch eingehen.
Sehen wir uns die Parameterliste der Add-Methode an. Im ersten Parameter erwartet die Methode eine Zeichenfolge. Hier geben wir den gleichen Parameterbezeichner an, den wir auch im SQL-Statement benutzt haben. Die Gleichnamigkeit bedeutet auch, dass die Reihenfolge der Parameter in der Auflistung nicht der Reihenfolge in der parametrisierten Abfrage entsprechen muss. Der zweite Parameter erwartet den Typ des Parameters. Da wir den Parameter an die Datenbank schicken, muss der Typ dem entsprechen, den die Datenbank erwartet. ADO.NET stellt in der Enumeration SqlDbType eine lange Liste von Konstanten bereit, um den .NET-Typ passend umzuwandeln. Der dritte und letzte Parameter verlangt die Breite der Spalte.
Zum Füllen des Parameters wird der Eigenschaft Value des Parameter-Objekts der entsprechende Wert zugewiesen:
| cmd.Parameters("@Zuname").Value = "Dull" |
Wir rufen den Indexer der Parameter-Collection auf und übergeben den Bezeichner des Parameters. Alternativ können wir auch den Index des Parameter-Objekts in der Auflistung verwenden.
Bisher haben Sie das SqlCommand-Objekt nur innerhalb einer Konsolenanwendung aktiv gesehen. Im Forms Designer geht alles mit Hilfe diverser Assistenten etwas einfacher. Dazu ziehen Sie ein SqlCommand-Objekt aus der Lasche Daten der Toolbox auf den Designer. Gegebenenfalls müssen Sie das Control erst der Toolbox hinzufügen, da es standardmäßig nicht sofort angeboten wird. Das SqlCommand-Objekt wird ebenfalls im Komponentenfach abgelegt. Zunächst ist es wichtig, dem SqlCommand mitzuteilen, auf welcher Verbindung das Kommando abgesetzt werden soll. Im Eigenschaftsfenster geben Sie diese daher unter Connection an. Es öffnet sich eine Drop-down-Liste, aus der die eventuell vorher definierte Verbindung mit dem SqlConnection-Control ausgewählt werden kann. Sie können aber auch direkt eine neue Verbindung definieren.
Im zweiten Schritt teilen Sie dem SqlCommand-Objekt den Befehl mit, den es ausführen soll. Markieren Sie die Eigenschaft CommandText im Eigenschaftsfenster, wird in der Wertespalte der Eigenschaft eine Schaltfläche mit drei Punkten angezeigt. Klicken Sie auf diese Schaltfläche, wird automatisch der Dialog Abfrage-Generator geöffnet, wie in Abbildung 26.2 zu sehen ist. Über diesem wird direkt nach dem Öffnen zusätzlich ein weiterer Dialog angezeigt, in dem Sie auf die Tabelle (oder auch die Tabellen) doppelt klicken, deren Daten abgefragt werden sollen.
Nach der Wahl der Tabelle(n) und dem Schließen des Fensters Tabelle hinzufügen wird im obersten Teilbereich des Abfrage-Generators die Tabelle mit allen ihren Spalten angezeigt. Markieren Sie die Spalten, die Sie für Ihre Abfrage benötigen. In der Abbildung 26.2 sind es die Spalten au_id, au_lname und au_fname.

Hier klicken, um das Bild zu Vergrößern
Abbildung 26.2 Der »Abfrage-Generator«
Unterhalb des Auswahlbereichs werden alle ausgewählten Spalten in einer Tabelle aufgelistet. Sie haben in dieser Tabelle sogar die Möglichkeit, die Sortierreihenfolge oder Auswahlkriterien der Abfrage festzulegen. Tragen Sie für die Spalte au_lname unter Filter beispielsweise »=@Zuname« ein, haben Sie einen Parameter festgelegt. Dieser lässt sich anschließend im Eigenschaftsfenster des SqlCommand-Steuerelements unter der Eigenschaft Parameters individuell einstellen. Das Ergebnis wird als SQL-Statement in einem weiteren Teilfenster des Abfrage-Generators ausgegeben.
Die Abfrage lässt sich aus dem Abfrage-Generator heraus testen. Klicken Sie dazu auf die Schaltfläche Abfrage ausführen, die das Resultat im untersten Teilbereich des Generators ausgibt.
| Tipp |
|
Der Abfrage-Generator hilft nicht nur dabei, SELECT-Auswahlabfragen auf grafisch orientierte Weise zu erstellen. Sie können über das Menüelement Typ ändern auch Aktionsabfragen erstellen. |
| << zurück |
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken.
Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die
gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich
geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung,
Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.